// CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.09 (2011-10-15)
// This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com)
CodeMirror.defineMode("perl", function (config, parserConfig) {
  // http://perldoc.perl.org
  var PERL = {              //   null - magic touch
    //   1 - keyword
    //   2 - def
    //   3 - atom
    //   4 - operator
    //   5 - variable-2 (predefined)
    //   [x,y] - x=1,2,3; y=must be defined if x{...}
    //	PERL operators
    '->':4,
    '++':4,
    '--':4,
    '**':4,
    //   ! ~ \ and unary + and -
    '=~':4,
    '!~':4,
    '*':4,
    '/':4,
    '%':4,
    'x':4,
    '+':4,
    '-':4,
    '.':4,
    '<<':4,
    '>>':4,
    //   named unary operators
    '<':4,
    '>':4,
    '<=':4,
    '>=':4,
    'lt':4,
    'gt':4,
    'le':4,
    'ge':4,
    '==':4,
    '!=':4,
    '<=>':4,
    'eq':4,
    'ne':4,
    'cmp':4,
    '~~':4,
    '&':4,
    '|':4,
    '^':4,
    '&&':4,
    '||':4,
    '//':4,
    '..':4,
    '...':4,
    '?':4,
    ':':4,
    '=':4,
    '+=':4,
    '-=':4,
    '*=':4, //   etc. ???
    ',':4,
    '=>':4,
    '::':4,
    //   list operators (rightward)
    'not':4,
    'and':4,
    'or':4,
    'xor':4,
    //	PERL predefined variables (I know, what this is a paranoid idea, but may be needed for people, who learn PERL, and for me as well, ...and may be for you?;)
    'BEGIN':[5, 1],
    'END':[5, 1],
    'PRINT':[5, 1],
    'PRINTF':[5, 1],
    'GETC':[5, 1],
    'READ':[5, 1],
    'READLINE':[5, 1],
    'DESTROY':[5, 1],
    'TIE':[5, 1],
    'TIEHANDLE':[5, 1],
    'UNTIE':[5, 1],
    'STDIN':5,
    'STDIN_TOP':5,
    'STDOUT':5,
    'STDOUT_TOP':5,
    'STDERR':5,
    'STDERR_TOP':5,
    '$ARG':5,
    '$_':5,
    '@ARG':5,
    '@_':5,
    '$LIST_SEPARATOR':5,
    '$"':5,
    '$PROCESS_ID':5,
    '$PID':5,
    '$$':5,
    '$REAL_GROUP_ID':5,
    '$GID':5,
    '$(':5,
    '$EFFECTIVE_GROUP_ID':5,
    '$EGID':5,
    '$)':5,
    '$PROGRAM_NAME':5,
    '$0':5,
    '$SUBSCRIPT_SEPARATOR':5,
    '$SUBSEP':5,
    '$;':5,
    '$REAL_USER_ID':5,
    '$UID':5,
    '$<':5,
    '$EFFECTIVE_USER_ID':5,
    '$EUID':5,
    '$>':5,
    '$a':5,
    '$b':5,
    '$COMPILING':5,
    '$^C':5,
    '$DEBUGGING':5,
    '$^D':5,
    '${^ENCODING}':5,
    '$ENV':5,
    '%ENV':5,
    '$SYSTEM_FD_MAX':5,
    '$^F':5,
    '@F':5,
    '${^GLOBAL_PHASE}':5,
    '$^H':5,
    '%^H':5,
    '@INC':5,
    '%INC':5,
    '$INPLACE_EDIT':5,
    '$^I':5,
    '$^M':5,
    '$OSNAME':5,
    '$^O':5,
    '${^OPEN}':5,
    '$PERLDB':5,
    '$^P':5,
    '$SIG':5,
    '%SIG':5,
    '$BASETIME':5,
    '$^T':5,
    '${^TAINT}':5,
    '${^UNICODE}':5,
    '${^UTF8CACHE}':5,
    '${^UTF8LOCALE}':5,
    '$PERL_VERSION':5,
    '$^V':5,
    '${^WIN32_SLOPPY_STAT}':5,
    '$EXECUTABLE_NAME':5,
    '$^X':5,
    '$1':5, // - regexp $1, $2...
    '$MATCH':5,
    '$&':5,
    '${^MATCH}':5,
    '$PREMATCH':5,
    '$`':5,
    '${^PREMATCH}':5,
    '$POSTMATCH':5,
    "$'":5,
    '${^POSTMATCH}':5,
    '$LAST_PAREN_MATCH':5,
    '$+':5,
    '$LAST_SUBMATCH_RESULT':5,
    '$^N':5,
    '@LAST_MATCH_END':5,
    '@+':5,
    '%LAST_PAREN_MATCH':5,
    '%+':5,
    '@LAST_MATCH_START':5,
    '@-':5,
    '%LAST_MATCH_START':5,
    '%-':5,
    '$LAST_REGEXP_CODE_RESULT':5,
    '$^R':5,
    '${^RE_DEBUG_FLAGS}':5,
    '${^RE_TRIE_MAXBUF}':5,
    '$ARGV':5,
    '@ARGV':5,
    'ARGV':5,
    'ARGVOUT':5,
    '$OUTPUT_FIELD_SEPARATOR':5,
    '$OFS':5,
    '$,':5,
    '$INPUT_LINE_NUMBER':5,
    '$NR':5,
    '$.':5,
    '$INPUT_RECORD_SEPARATOR':5,
    '$RS':5,
    '$/':5,
    '$OUTPUT_RECORD_SEPARATOR':5,
    '$ORS':5,
    '$\\':5,
    '$OUTPUT_AUTOFLUSH':5,
    '$|':5,
    '$ACCUMULATOR':5,
    '$^A':5,
    '$FORMAT_FORMFEED':5,
    '$^L':5,
    '$FORMAT_PAGE_NUMBER':5,
    '$%':5,
    '$FORMAT_LINES_LEFT':5,
    '$-':5,
    '$FORMAT_LINE_BREAK_CHARACTERS':5,
    '$:':5,
    '$FORMAT_LINES_PER_PAGE':5,
    '$=':5,
    '$FORMAT_TOP_NAME':5,
    '$^':5,
    '$FORMAT_NAME':5,
    '$~':5,
    '${^CHILD_ERROR_NATIVE}':5,
    '$EXTENDED_OS_ERROR':5,
    '$^E':5,
    '$EXCEPTIONS_BEING_CAUGHT':5,
    '$^S':5,
    '$WARNING':5,
    '$^W':5,
    '${^WARNING_BITS}':5,
    '$OS_ERROR':5,
    '$ERRNO':5,
    '$!':5,
    '%OS_ERROR':5,
    '%ERRNO':5,
    '%!':5,
    '$CHILD_ERROR':5,
    '$?':5,
    '$EVAL_ERROR':5,
    '$@':5,
    '$OFMT':5,
    '$#':5,
    '$*':5,
    '$ARRAY_BASE':5,
    '$[':5,
    '$OLD_PERL_VERSION':5,
    '$]':5,
    //	PERL blocks
    'if':[1, 1],
    elsif:[1, 1],
    'else':[1, 1],
    'while':[1, 1],
    unless:[1, 1],
    'for':[1, 1],
    foreach:[1, 1],
    //	PERL functions
    'abs':1, // - absolute value function
    accept:1, // - accept an incoming socket connect
    alarm:1, // - schedule a SIGALRM
    'atan2':1, // - arctangent of Y/X in the range -PI to PI
    bind:1, // - binds an address to a socket
    binmode:1, // - prepare binary files for I/O
    bless:1, // - create an object
    bootstrap:1, //
    'break':1, // - break out of a "given" block
    caller:1, // - get context of the current subroutine call
    chdir:1, // - change your current working directory
    chmod:1, // - changes the permissions on a list of files
    chomp:1, // - remove a trailing record separator from a string
    chop:1, // - remove the last character from a string
    chown:1, // - change the owership on a list of files
    chr:1, // - get character this number represents
    chroot:1, // - make directory new root for path lookups
    close:1, // - close file (or pipe or socket) handle
    closedir:1, // - close directory handle
    connect:1, // - connect to a remote socket
    'continue':[1, 1], // - optional trailing block in a while or foreach
    'cos':1, // - cosine function
    crypt:1, // - one-way passwd-style encryption
    dbmclose:1, // - breaks binding on a tied dbm file
    dbmopen:1, // - create binding on a tied dbm file
    'default':1, //
    defined:1, // - test whether a value, variable, or function is defined
    'delete':1, // - deletes a value from a hash
    die:1, // - raise an exception or bail out
    'do':1, // - turn a BLOCK into a TERM
    dump:1, // - create an immediate core dump
    each:1, // - retrieve the next key/value pair from a hash
    endgrent:1, // - be done using group file
    endhostent:1, // - be done using hosts file
    endnetent:1, // - be done using networks file
    endprotoent:1, // - be done using protocols file
    endpwent:1, // - be done using passwd file
    endservent:1, // - be done using services file
    eof:1, // - test a filehandle for its end
    'eval':1, // - catch exceptions or compile and run code
    'exec':1, // - abandon this program to run another
    exists:1, // - test whether a hash key is present
    exit:1, // - terminate this program
    'exp':1, // - raise I to a power
    fcntl:1, // - file control system call
    fileno:1, // - return file descriptor from filehandle
    flock:1, // - lock an entire file with an advisory lock
    fork:1, // - create a new process just like this one
    format:1, // - declare a picture format with use by the write() function
    formline:1, // - internal function used for formats
    getc:1, // - get the next character from the filehandle
    getgrent:1, // - get next group record
    getgrgid:1, // - get group record given group user ID
    getgrnam:1, // - get group record given group name
    gethostbyaddr:1, // - get host record given its address
    gethostbyname:1, // - get host record given name
    gethostent:1, // - get next hosts record
    getlogin:1, // - return who logged in at this tty
    getnetbyaddr:1, // - get network record given its address
    getnetbyname:1, // - get networks record given name
    getnetent:1, // - get next networks record
    getpeername:1, // - find the other end of a socket connection
    getpgrp:1, // - get process group
    getppid:1, // - get parent process ID
    getpriority:1, // - get current nice value
    getprotobyname:1, // - get protocol record given name
    getprotobynumber:1, // - get protocol record numeric protocol
    getprotoent:1, // - get next protocols record
    getpwent:1, // - get next passwd record
    getpwnam:1, // - get passwd record given user login name
    getpwuid:1, // - get passwd record given user ID
    getservbyname:1, // - get services record given its name
    getservbyport:1, // - get services record given numeric port
    getservent:1, // - get next services record
    getsockname:1, // - retrieve the sockaddr for a given socket
    getsockopt:1, // - get socket options on a given socket
    given:1, //
    glob:1, // - expand filenames using wildcards
    gmtime:1, // - convert UNIX time into record or string using Greenwich time
    'goto':1, // - create spaghetti code
    grep:1, // - locate elements in a list test true against a given criterion
    hex:1, // - convert a string to a hexadecimal number
    'import':1, // - patch a module's namespace into your own
    index:1, // - find a substring within a string
    int:1, // - get the integer portion of a number
    ioctl:1, // - system-dependent device control system call
    'join':1, // - join a list into a string using a separator
    keys:1, // - retrieve list of indices from a hash
    kill:1, // - send a signal to a process or process group
    last:1, // - exit a block prematurely
    lc:1, // - return lower-case version of a string
    lcfirst:1, // - return a string with just the next letter in lower case
    length:1, // - return the number of bytes in a string
    'link':1, // - create a hard link in the filesytem
    listen:1, // - register your socket as a server
    local:2, // - create a temporary value for a global variable (dynamic scoping)
    localtime:1, // - convert UNIX time into record or string using local time
    lock:1, // - get a thread lock on a variable, subroutine, or method
    'log':1, // - retrieve the natural logarithm for a number
    lstat:1, // - stat a symbolic link
    m:null, // - match a string with a regular expression pattern
    map:1, // - apply a change to a list to get back a new list with the changes
    mkdir:1, // - create a directory
    msgctl:1, // - SysV IPC message control operations
    msgget:1, // - get SysV IPC message queue
    msgrcv:1, // - receive a SysV IPC message from a message queue
    msgsnd:1, // - send a SysV IPC message to a message queue
    my:2, // - declare and assign a local variable (lexical scoping)
    'new':1, //
    next:1, // - iterate a block prematurely
    no:1, // - unimport some module symbols or semantics at compile time
    oct:1, // - convert a string to an octal number
    open:1, // - open a file, pipe, or descriptor
    opendir:1, // - open a directory
    ord:1, // - find a character's numeric representation
    our:2, // - declare and assign a package variable (lexical scoping)
    pack:1, // - convert a list into a binary representation
    'package':1, // - declare a separate global namespace
    pipe:1, // - open a pair of connected filehandles
    pop:1, // - remove the last element from an array and return it
    pos:1, // - find or set the offset for the last/next m//g search
    print:1, // - output a list to a filehandle
    printf:1, // - output a formatted list to a filehandle
    prototype:1, // - get the prototype (if any) of a subroutine
    push:1, // - append one or more elements to an array
    q:null, // - singly quote a string
    qq:null, // - doubly quote a string
    qr:null, // - Compile pattern
    quotemeta:null, // - quote regular expression magic characters
    qw:null, // - quote a list of words
    qx:null, // - backquote quote a string
    rand:1, // - retrieve the next pseudorandom number
    read:1, // - fixed-length buffered input from a filehandle
    readdir:1, // - get a directory from a directory handle
    readline:1, // - fetch a record from a file
    readlink:1, // - determine where a symbolic link is pointing
    readpipe:1, // - execute a system command and collect standard output
    recv:1, // - receive a message over a Socket
    redo:1, // - start this loop iteration over again
    ref:1, // - find out the type of thing being referenced
    rename:1, // - change a filename
    require:1, // - load in external functions from a library at runtime
    reset:1, // - clear all variables of a given name
    'return':1, // - get out of a function early
    reverse:1, // - flip a string or a list
    rewinddir:1, // - reset directory handle
    rindex:1, // - right-to-left substring search
    rmdir:1, // - remove a directory
    s:null, // - replace a pattern with a string
    say:1, // - print with newline
    scalar:1, // - force a scalar context
    seek:1, // - reposition file pointer for random-access I/O
    seekdir:1, // - reposition directory pointer
    select:1, // - reset default output or do I/O multiplexing
    semctl:1, // - SysV semaphore control operations
    semget:1, // - get set of SysV semaphores
    semop:1, // - SysV semaphore operations
    send:1, // - send a message over a socket
    setgrent:1, // - prepare group file for use
    sethostent:1, // - prepare hosts file for use
    setnetent:1, // - prepare networks file for use
    setpgrp:1, // - set the process group of a process
    setpriority:1, // - set a process's nice value
    setprotoent:1, // - prepare protocols file for use
    setpwent:1, // - prepare passwd file for use
    setservent:1, // - prepare services file for use
    setsockopt:1, // - set some socket options
    shift:1, // - remove the first element of an array, and return it
    shmctl:1, // - SysV shared memory operations
    shmget:1, // - get SysV shared memory segment identifier
    shmread:1, // - read SysV shared memory
    shmwrite:1, // - write SysV shared memory
    shutdown:1, // - close down just half of a socket connection
    'sin':1, // - return the sine of a number
    sleep:1, // - block for some number of seconds
    socket:1, // - create a socket
    socketpair:1, // - create a pair of sockets
    'sort':1, // - sort a list of values
    splice:1, // - add or remove elements anywhere in an array
    'split':1, // - split up a string using a regexp delimiter
    sprintf:1, // - formatted print into a string
    'sqrt':1, // - square root function
    srand:1, // - seed the random number generator
    stat:1, // - get a file's status information
    state:1, // - declare and assign a state variable (persistent lexical scoping)
    study:1, // - optimize input data for repeated searches
    'sub':1, // - declare a subroutine, possibly anonymously
    'substr':1, // - get or alter a portion of a stirng
    symlink:1, // - create a symbolic link to a file
    syscall:1, // - execute an arbitrary system call
    sysopen:1, // - open a file, pipe, or descriptor
    sysread:1, // - fixed-length unbuffered input from a filehandle
    sysseek:1, // - position I/O pointer on handle used with sysread and syswrite
    system:1, // - run a separate program
    syswrite:1, // - fixed-length unbuffered output to a filehandle
    tell:1, // - get current seekpointer on a filehandle
    telldir:1, // - get current seekpointer on a directory handle
    tie:1, // - bind a variable to an object class
    tied:1, // - get a reference to the object underlying a tied variable
    time:1, // - return number of seconds since 1970
    times:1, // - return elapsed time for self and child processes
    tr:null, // - transliterate a string
    truncate:1, // - shorten a file
    uc:1, // - return upper-case version of a string
    ucfirst:1, // - return a string with just the next letter in upper case
    umask:1, // - set file creation mode mask
    undef:1, // - remove a variable or function definition
    unlink:1, // - remove one link to a file
    unpack:1, // - convert binary structure into normal perl variables
    unshift:1, // - prepend more elements to the beginning of a list
    untie:1, // - break a tie binding to a variable
    use:1, // - load in a module at compile time
    utime:1, // - set a file's last access and modify times
    values:1, // - return a list of the values in a hash
    vec:1, // - test or set particular bits in a string
    wait:1, // - wait for any child process to die
    waitpid:1, // - wait for a particular child process to die
    wantarray:1, // - get void vs scalar vs list context of current subroutine call
    warn:1, // - print debugging info
    when:1, //
    write:1, // - print a picture record
    y:null};	// - transliterate a string

  var RXstyle = "string-2";
  var RXmodifiers = /[goseximacplud]/;		// NOTE: "m", "s", "y" and "tr" need to correct real modifiers for each regexp type

  function tokenChain(stream, state, chain, style, tail) {  // NOTE: chain.length > 2 is not working now (it's for s[...][...]geos;)
    state.chain = null;                               //                                                          12   3tail
    state.style = null;
    state.tail = null;
    state.tokenize = function (stream, state) {
      var e = false, c, i = 0;
      while (c = stream.next()) {
        if (c === chain[i] && !e) {
          if (chain[++i] !== undefined) {
            state.chain = chain[i];
            state.style = style;
            state.tail = tail
          }
          else if (tail)
            stream.eatWhile(tail);
          state.tokenize = tokenPerl;
          return style
        }
        e = !e && c == "\\"
      }
      return style
    };
    return state.tokenize(stream, state)
  }

  function tokenSOMETHING(stream, state, string) {
    state.tokenize = function (stream, state) {
      if (stream.string == string)
        state.tokenize = tokenPerl;
      stream.skipToEnd();
      return "string"
    };
    return state.tokenize(stream, state)
  }

  function tokenPerl(stream, state) {
    if (stream.eatSpace())
      return null;
    if (state.chain)
      return tokenChain(stream, state, state.chain, state.style, state.tail);
    if (stream.match(/^\-?[\d\.]/, false))
      if (stream.match(/^(\-?(\d*\.\d+(e[+-]?\d+)?|\d+\.\d*)|0x[\da-fA-F]+|0b[01]+|\d+(e[+-]?\d+)?)/))
        return 'number';
    if (stream.match(/^<<(?=\w)/)) {      // NOTE: <<SOMETHING\n...\nSOMETHING\n
      stream.eatWhile(/\w/);
      return tokenSOMETHING(stream, state, stream.current().substr(2))
    }
    if (stream.sol() && stream.match(/^\=item(?!\w)/)) {// NOTE: \n=item...\n=cut\n
      return tokenSOMETHING(stream, state, '=cut')
    }
    var ch = stream.next();
    if (ch == '"' || ch == "'") {        // NOTE: ' or " or <<'SOMETHING'\n...\nSOMETHING\n or <<"SOMETHING"\n...\nSOMETHING\n
      if (stream.prefix(3) == "<<" + ch) {
        var p = stream.pos;
        stream.eatWhile(/\w/);
        var n = stream.current().substr(1);
        if (n && stream.eat(ch))
          return tokenSOMETHING(stream, state, n);
        stream.pos = p
      }
      return tokenChain(stream, state, [ch], "string")
    }
    if (ch == "q") {
      var c = stream.look(-2);
      if (!(c && /\w/.test(c))) {
        c = stream.look(0);
        if (c == "x") {
          c = stream.look(1);
          if (c == "(") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [")"], RXstyle, RXmodifiers)
          }
          if (c == "[") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["]"], RXstyle, RXmodifiers)
          }
          if (c == "{") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["}"], RXstyle, RXmodifiers)
          }
          if (c == "<") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [">"], RXstyle, RXmodifiers)
          }
          if (/[\^'"!~\/]/.test(c)) {
            stream.eatSuffix(1);
            return tokenChain(stream, state, [stream.eat(c)], RXstyle, RXmodifiers)
          }
        }
        else if (c == "q") {
          c = stream.look(1);
          if (c == "(") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [")"], "string")
          }
          if (c == "[") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["]"], "string")
          }
          if (c == "{") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["}"], "string")
          }
          if (c == "<") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [">"], "string")
          }
          if (/[\^'"!~\/]/.test(c)) {
            stream.eatSuffix(1);
            return tokenChain(stream, state, [stream.eat(c)], "string")
          }
        }
        else if (c == "w") {
          c = stream.look(1);
          if (c == "(") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [")"], "bracket")
          }
          if (c == "[") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["]"], "bracket")
          }
          if (c == "{") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["}"], "bracket")
          }
          if (c == "<") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [">"], "bracket")
          }
          if (/[\^'"!~\/]/.test(c)) {
            stream.eatSuffix(1);
            return tokenChain(stream, state, [stream.eat(c)], "bracket")
          }
        }
        else if (c == "r") {
          c = stream.look(1);
          if (c == "(") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [")"], RXstyle, RXmodifiers)
          }
          if (c == "[") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["]"], RXstyle, RXmodifiers)
          }
          if (c == "{") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, ["}"], RXstyle, RXmodifiers)
          }
          if (c == "<") {
            stream.eatSuffix(2);
            return tokenChain(stream, state, [">"], RXstyle, RXmodifiers)
          }
          if (/[\^'"!~\/]/.test(c)) {
            stream.eatSuffix(1);
            return tokenChain(stream, state, [stream.eat(c)], RXstyle, RXmodifiers)
          }
        }
        else if (/[\^'"!~\/(\[{<]/.test(c)) {
          if (c == "(") {
            stream.eatSuffix(1);
            return tokenChain(stream, state, [")"], "string")
          }
          if (c == "[") {
            stream.eatSuffix(1);
            return tokenChain(stream, state, ["]"], "string")
          }
          if (c == "{") {
            stream.eatSuffix(1);
            return tokenChain(stream, state, ["}"], "string")
          }
          if (c == "<") {
            stream.eatSuffix(1);
            return tokenChain(stream, state, [">"], "string")
          }
          if (/[\^'"!~\/]/.test(c)) {
            return tokenChain(stream, state, [stream.eat(c)], "string")
          }
        }
      }
    }
    if (ch == "m") {
      var c = stream.look(-2);
      if (!(c && /\w/.test(c))) {
        c = stream.eat(/[(\[{<\^'"!~\/]/);
        if (c) {
          if (/[\^'"!~\/]/.test(c)) {
            return tokenChain(stream, state, [c], RXstyle, RXmodifiers)
          }
          if (c == "(") {
            return tokenChain(stream, state, [")"], RXstyle, RXmodifiers)
          }
          if (c == "[") {
            return tokenChain(stream, state, ["]"], RXstyle, RXmodifiers)
          }
          if (c == "{") {
            return tokenChain(stream, state, ["}"], RXstyle, RXmodifiers)
          }
          if (c == "<") {
            return tokenChain(stream, state, [">"], RXstyle, RXmodifiers)
          }
        }
      }
    }
    if (ch == "s") {
      var c = /[\/>\]})\w]/.test(stream.look(-2));
      if (!c) {
        c = stream.eat(/[(\[{<\^'"!~\/]/);
        if (c) {
          if (c == "[")
            return tokenChain(stream, state, ["]", "]"], RXstyle, RXmodifiers);
          if (c == "{")
            return tokenChain(stream, state, ["}", "}"], RXstyle, RXmodifiers);
          if (c == "<")
            return tokenChain(stream, state, [">", ">"], RXstyle, RXmodifiers);
          if (c == "(")
            return tokenChain(stream, state, [")", ")"], RXstyle, RXmodifiers);
          return tokenChain(stream, state, [c, c], RXstyle, RXmodifiers)
        }
      }
    }
    if (ch == "y") {
      var c = /[\/>\]})\w]/.test(stream.look(-2));
      if (!c) {
        c = stream.eat(/[(\[{<\^'"!~\/]/);
        if (c) {
          if (c == "[")
            return tokenChain(stream, state, ["]", "]"], RXstyle, RXmodifiers);
          if (c == "{")
            return tokenChain(stream, state, ["}", "}"], RXstyle, RXmodifiers);
          if (c == "<")
            return tokenChain(stream, state, [">", ">"], RXstyle, RXmodifiers);
          if (c == "(")
            return tokenChain(stream, state, [")", ")"], RXstyle, RXmodifiers);
          return tokenChain(stream, state, [c, c], RXstyle, RXmodifiers)
        }
      }
    }
    if (ch == "t") {
      var c = /[\/>\]})\w]/.test(stream.look(-2));
      if (!c) {
        c = stream.eat("r");
        if (c) {
          c = stream.eat(/[(\[{<\^'"!~\/]/);
          if (c) {
            if (c == "[")
              return tokenChain(stream, state, ["]", "]"], RXstyle, RXmodifiers);
            if (c == "{")
              return tokenChain(stream, state, ["}", "}"], RXstyle, RXmodifiers);
            if (c == "<")
              return tokenChain(stream, state, [">", ">"], RXstyle, RXmodifiers);
            if (c == "(")
              return tokenChain(stream, state, [")", ")"], RXstyle, RXmodifiers);
            return tokenChain(stream, state, [c, c], RXstyle, RXmodifiers)
          }
        }
      }
    }
    if (ch == "`") {
      return tokenChain(stream, state, [ch], "variable-2")
    }
    if (ch == "/") {
      return tokenChain(stream, state, [ch], RXstyle, RXmodifiers)
    }
    if (ch == "$") {
      var p = stream.pos;
      if (stream.eatWhile(/\d/) || stream.eat("{") && stream.eatWhile(/\d/) && stream.eat("}"))
        return "variable-2";
      else
        stream.pos = p
    }
    if (/[$@%]/.test(ch)) {
      var p = stream.pos;
      if (stream.eat("^") && stream.eat(/[A-Z]/) || !/[@$%&]/.test(stream.look(-2)) && stream.eat(/[=|\\\-#?@;:&`~\^!\[\]*'"$+.,\/<>()]/)) {
        var c = stream.current();
        if (PERL[c])
          return "variable-2"
      }
      stream.pos = p
    }
    if (/[$@%&]/.test(ch)) {
      if (stream.eatWhile(/[\w$\[\]]/) || stream.eat("{") && stream.eatWhile(/[\w$\[\]]/) && stream.eat("}")) {
        var c = stream.current();
        if (PERL[c])
          return "variable-2";
        else
          return "variable"
      }
    }
    if (ch == "#") {
      if (stream.look(-2) != "$") {
        stream.skipToEnd();
        return "comment"
      }
    }
    if (/[:+\-\^*$&%@=<>!?|\/~\.]/.test(ch)) {
      var p = stream.pos;
      stream.eatWhile(/[:+\-\^*$&%@=<>!?|\/~\.]/);
      if (PERL[stream.current()])
        return "operator";
      else
        stream.pos = p
    }
    if (ch == "_") {
      if (stream.pos == 1) {
        if (stream.suffix(6) == "_END__") {
          return tokenChain(stream, state, ['\0'], "comment")
        }
        else if (stream.suffix(7) == "_DATA__") {
          return tokenChain(stream, state, ['\0'], "variable-2")
        }
        else if (stream.suffix(7) == "_C__") {
          return tokenChain(stream, state, ['\0'], "string")
        }
      }
    }
    if (/\w/.test(ch)) {
      var p = stream.pos;
      if (stream.look(-2) == "{" && (stream.look(0) == "}" || stream.eatWhile(/\w/) && stream.look(0) == "}"))
        return "string";
      else
        stream.pos = p
    }
    if (/[A-Z]/.test(ch)) {
      var l = stream.look(-2);
      var p = stream.pos;
      stream.eatWhile(/[A-Z_]/);
      if (/[\da-z]/.test(stream.look(0))) {
        stream.pos = p
      }
      else {
        var c = PERL[stream.current()];
        if (!c)
          return "meta";
        if (c[1])
          c = c[0];
        if (l != ":") {
          if (c == 1)
            return "keyword";
          else if (c == 2)
            return "def";
          else if (c == 3)
            return "atom";
          else if (c == 4)
            return "operator";
          else if (c == 5)
            return "variable-2";
          else
            return "meta"
        }
        else
          return "meta"
      }
    }
    if (/[a-zA-Z_]/.test(ch)) {
      var l = stream.look(-2);
      stream.eatWhile(/\w/);
      var c = PERL[stream.current()];
      if (!c)
        return "meta";
      if (c[1])
        c = c[0];
      if (l != ":") {
        if (c == 1)
          return "keyword";
        else if (c == 2)
          return "def";
        else if (c == 3)
          return "atom";
        else if (c == 4)
          return "operator";
        else if (c == 5)
          return "variable-2";
        else
          return "meta"
      }
      else
        return "meta"
    }
    return null
  }

  return{
    startState:function () {
      return{
        tokenize:tokenPerl,
        chain:null,
        style:null,
        tail:null}
    },
    token:function (stream, state) {
      return (state.tokenize || tokenPerl)(stream, state)
    },
    electricChars:"{}"}
});

CodeMirror.defineMIME("text/x-perl", "perl");

// it's like "peek", but need for look-ahead or look-behind if index < 0
CodeMirror.StringStream.prototype.look = function (c) {
  return this.string.charAt(this.pos + (c || 0))
};

// return a part of prefix of current stream from current position
CodeMirror.StringStream.prototype.prefix = function (c) {
  var x = this.pos - c;
  return this.string.substr((x >= 0 ? x : 0), c)
};

// return a part of suffix of current stream from current position
CodeMirror.StringStream.prototype.suffix = function (c) {
  var y = this.string.length;
  var x = y - this.pos + 1;
  return this.string.substr(this.pos, (c && c < y ? c : x))
};

// return a part of suffix of current stream from current position and change current position
CodeMirror.StringStream.prototype.nsuffix = function (c) {
  var p = this.pos;
  var l = c || (this.string.length - this.pos + 1);
  this.pos += l;
  return this.string.substr(p, l)
};

// eating and vomiting a part of stream from current position
CodeMirror.StringStream.prototype.eatSuffix = function (c) {
  var x = this.pos + c;
  var y;
  if (x <= 0)
    this.pos = 0;
  else if (x >= (y = this.string.length - 1))
    this.pos = y;
  else
    this.pos = x
};
